001 /*
002 * Copyright 2004-2005 Stephen J. McConnell.
003 * Copyright 2004 Niclas Hedhman
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
013 * implied.
014 *
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package net.dpml.transit.artifact;
020
021 import java.io.IOException;
022 import java.io.PrintWriter;
023
024 import java.net.URL;
025 import java.net.URLConnection;
026 import java.net.URLStreamHandler;
027 import java.security.AccessController;
028 import java.security.PrivilegedAction;
029
030 import net.dpml.transit.Transit;
031 import net.dpml.transit.TransitRuntimeException;
032 import net.dpml.transit.SecuredTransitContext;
033
034 /**
035 * The artifact URL protocol handler.
036 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
037 * @version 1.0.2
038 */
039 public class Handler extends URLStreamHandler
040 {
041 // ------------------------------------------------------------------------
042 // static
043 // ------------------------------------------------------------------------
044
045 /**
046 * Default buffer size.
047 */
048 private static final int BUFFER_SIZE = 100;
049
050 // ------------------------------------------------------------------------
051 // state
052 // ------------------------------------------------------------------------
053
054 /**
055 * The transit context.
056 */
057 private SecuredTransitContext m_context;
058
059 // ------------------------------------------------------------------------
060 // constructor
061 // ------------------------------------------------------------------------
062
063 /**
064 * Creation of a new transit artifact protocol handler.
065 */
066 public Handler()
067 {
068 try
069 {
070 Transit.getInstance();
071 m_context = SecuredTransitContext.getInstance();
072 }
073 catch( RuntimeException e )
074 {
075 e.printStackTrace();
076 throw e;
077 }
078 }
079
080 // ------------------------------------------------------------------------
081 // implementation
082 // ------------------------------------------------------------------------
083
084 /**
085 * Opens a connection to the specified URL.
086 *
087 * @param url A URL to open a connection to.
088 * @return The established connection.
089 * @throws IOException If a connection failure occurs.
090 */
091 protected URLConnection openConnection( final URL url )
092 throws IOException
093 {
094 return new ArtifactURLConnection( url, m_context );
095 }
096
097 /**
098 * Return the external representation of the supplied url.
099 * @param u the url
100 * @return a string representing of the url as an artifact uri
101 */
102 protected String toExternalForm( URL u )
103 {
104 StringBuffer buf = new StringBuffer( BUFFER_SIZE );
105 buf.append( u.getProtocol() );
106 buf.append( ":" );
107 String path = getRealPath( u );
108 if( path != null )
109 {
110 int lastPos = path.length() - 1;
111 if( path.charAt( lastPos ) == '/' )
112 {
113 buf.append( path.substring( 0, lastPos ) );
114 }
115 else
116 {
117 buf.append( path );
118 }
119 }
120
121 String internal = getInternalPath( u );
122 if( null != internal )
123 {
124 buf.append( internal );
125 }
126
127 String query = u.getQuery();
128 if( query != null )
129 {
130 buf.append( '?' );
131 buf.append( query );
132 }
133
134 String version = u.getUserInfo();
135 if( ( version != null ) && !"".equals( version ) )
136 {
137 buf.append( '#' );
138 buf.append( version );
139 }
140 String result = buf.toString();
141 buf.setLength( 0 );
142 return result;
143 }
144
145 /**
146 * Return the pure artifact path without any internal address.
147 * @param url the url to evaluate
148 * @return the pure path
149 */
150 private String getRealPath( URL url )
151 {
152 String path = url.getPath();
153 if( null == path )
154 {
155 return null;
156 }
157 int index = path.indexOf( "!" );
158 if( index < 0 )
159 {
160 return path;
161 }
162 else
163 {
164 return path.substring( 0, index );
165 }
166 }
167
168 /**
169 * Return the value of an internal address associated with a path.
170 * @param url the url to evaluate
171 * @return the internal address of null if the url does not contain an internal address
172 */
173 private String getInternalPath( URL url )
174 {
175 String path = url.getPath();
176 if( null == path )
177 {
178 return null;
179 }
180 int index = path.indexOf( "!" );
181 if( index < 0 )
182 {
183 return null;
184 }
185 else
186 {
187 return path.substring( index );
188 }
189 }
190
191
192 /**
193 * Parse the supplied specification.
194 * @param dest the destination url to populate
195 * @param spec the supplied spec
196 * @param start the starting position
197 * @param limit the limit
198 */
199 protected void parseURL( final URL dest, String spec, int start, int limit )
200 {
201 try
202 {
203 final String protocol = dest.getProtocol();
204 String specPath = spec.substring( start, limit );
205 String path = dest.getPath();
206
207 // get any query info from the supplied spec
208
209 final String query = getQueryFragment( specPath );
210 if( null != query )
211 {
212 int n = specPath.indexOf( "?" );
213 if( n > -1 )
214 {
215 specPath = specPath.substring( 0, n );
216 }
217 }
218
219 // get the version using the url ref semantics
220
221 final String version = getRefFragment( specPath );
222 if( null != version )
223 {
224 int n = specPath.indexOf( "#" );
225 if( n > -1 )
226 {
227 specPath = specPath.substring( 0, n );
228 }
229 }
230
231 // setup the path
232
233 if( path == null )
234 {
235 path = specPath;
236 if( !path.endsWith( "/" ) && ( path.indexOf( "!" ) < 0 ) )
237 {
238 path = path + "/";
239 }
240 }
241 else
242 {
243 int lastPos = path.length() - 1;
244 if( path.charAt( lastPos ) == '/' )
245 {
246 path = path.substring( 0, lastPos );
247 }
248 if( specPath.charAt( 0 ) == '/' )
249 {
250 path = path + "!" + specPath;
251 }
252 else
253 {
254 path = path + "!/" + specPath;
255 }
256 }
257
258 // establish the artifact type
259
260 String type = dest.getUserInfo();
261 if( type == null )
262 {
263 if( limit < spec.length() )
264 {
265 type = spec.substring( limit + 1 );
266 }
267 }
268
269 // map features to url fields
270
271 final String user = type; // final String user = version;
272 final String authority = null;
273 final int port = -1;
274 final String host = null;
275 final String ref = version; // final String ref = null;
276 final String finalPath = path;
277 AccessController.doPrivileged(
278 new PrivilegedAction()
279 {
280 public Object run()
281 {
282 setURL( dest, protocol, host, port, authority, user, finalPath, query, ref );
283 return null;
284 }
285 }
286 );
287 }
288 catch( Throwable e )
289 {
290 try
291 {
292 PrintWriter log = Transit.getInstance().getLogWriter();
293 String message = "Unable to parse the URL: "
294 + dest + ", " + spec + ", " + start + ", " + limit;
295 log.println( message );
296 log.println( "---------------------------------------------------" );
297 e.printStackTrace( log );
298 }
299 catch( TransitRuntimeException f )
300 {
301 f.printStackTrace();
302 e.printStackTrace();
303 }
304 }
305 }
306
307 private String getQueryFragment( String path )
308 {
309 if( null == path )
310 {
311 return null;
312 }
313 else
314 {
315 int q = path.indexOf( "?" );
316 if( q > -1 )
317 {
318 return path.substring( q+1 );
319 }
320 else
321 {
322 return null;
323 }
324 }
325 }
326
327 private String getRefFragment( String path )
328 {
329 if( null == path )
330 {
331 return null;
332 }
333 else
334 {
335 int n = path.indexOf( "#" );
336 if( n > -1 )
337 {
338 return path.substring( n+1 );
339 }
340 else
341 {
342 return null;
343 }
344 }
345 }
346 }